package com.dayu.finecomm.iot;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.DefaultFullHttpRequest;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.dayu.finecomm.aop.ClassLevelAnnotation;
import com.dayu.finecomm.aop.MethodLevelAnnotation;
import com.dayu.finecomm.iot.device.IotPlatSyncDevice;
import com.dayu.finecomm.iot.ssl.SslContextFactory;

/**
 * IotHttpClient 同步获取响应
 * 
 * @author XuJialiang
 * @since 2017-09-06 15:45:12
 * @version V1.0.0
 */
@ClassLevelAnnotation
public class IotHttpClient {
	static Logger LOGGER = LoggerFactory.getLogger(IotHttpClient.class);

	private static final int ERROR_500 = 300500;
	private static final int ERROR_502 = 300502;

	private static final String ERROR_500_DESC = "Internal Server Error.";
	private static final String ERROR_502_DESC = "Connect iot plat failed.";

	/**
	 * Web Server URI
	 */
	private final String mRemoteHost;

	/**
	 * Web Server Port
	 */
	private final int mRemotePort;

	private final boolean mUseSSL;

	/**
	 * HTTP连接通道
	 */
	private ChannelFuture mFuture;

	/**
	 * IotPlatSyncDevice
	 */
	IotPlatSyncDevice mDevice;

	/**
	 * EventLoopGroup
	 */
	EventLoopGroup mGroup;

	public IotHttpClient(String host, int port) {
		mRemoteHost = host;
		mRemotePort = port;
		mUseSSL = true;
		mFuture = null;
		mDevice = null;
		mGroup = null;
	}

	public IotHttpClient(String host, int port, boolean useSSL) {
		mRemoteHost = host;
		mRemotePort = port;
		mUseSSL = useSSL;
		mFuture = null;
		mDevice = null;
		mGroup = null;
	}

	/**
	 * HTTP连接
	 * 
	 * @return 0 - Success -1 - Failed
	 * @throws Exception
	 */
	public int connect() {
		int result = -1;

		EventLoopGroup mGroup = new NioEventLoopGroup();

		try {
			CountDownLatch lathc = new CountDownLatch(1);
			if (mUseSSL) {
				mDevice = new IotPlatSyncDevice(lathc, 0,
						SslContextFactory.getIotContext());
			} else {
				mDevice = new IotPlatSyncDevice(lathc, 0, null);
			}
			Bootstrap bootstrap = new Bootstrap();
			bootstrap.group(mGroup);
			bootstrap.channel(NioSocketChannel.class);
			bootstrap.handler(mDevice);

			mFuture = bootstrap.connect(mRemoteHost, mRemotePort);

			result = 0;
		} catch (Exception e) {
			LOGGER.error("Connect: " + mRemoteHost + ", error: " + e);
			mGroup.shutdownGracefully();
			mGroup = null;
		}

		return result;
	}

	/**
	 * 同步请求HTTP Request
	 * 
	 * @param request
	 *            DefaultFullHttpRequest
	 * @return {"code":xxx, "body", content}
	 */
	@MethodLevelAnnotation
	public JSONObject IotRequest(DefaultFullHttpRequest request) {
		JSONObject jsonResult = null;

		try {
			Channel channel = mFuture.sync().channel();
			CountDownLatch lathc = new CountDownLatch(1);
			mDevice.resetLathc(lathc);

			LOGGER.info(request.toString());

			channel.write(request);
			channel.flush();

			boolean result = lathc.await(15, TimeUnit.SECONDS); // 等待15s超时

			if (true == result) {
				String content = mDevice.getServerResult();
				int code = mDevice.getServerCode();

				jsonResult = new JSONObject();
				jsonResult.put("code", code);

				JSONObject jsonContent = JSON.parseObject(content);
				jsonResult.put("body", jsonContent);
			} else {
				JSONObject body = new JSONObject();
				body.put("error_code", String.valueOf(ERROR_502));
				body.put("error_desc", ERROR_502_DESC);

				jsonResult = new JSONObject();
				jsonResult.put("code", 502);
				jsonResult.put("body", body);
			}
		} catch (Exception e) {
			LOGGER.error("Request: " + mRemoteHost + ", error: " + e);
			mGroup.shutdownGracefully();
			mGroup = null;

			JSONObject body = new JSONObject();
			body.put("error_code", String.valueOf(ERROR_500));
			body.put("error_desc", ERROR_500_DESC);

			jsonResult = new JSONObject();
			jsonResult.put("code", 500);
			jsonResult.put("body", body);
		}

		LOGGER.info(jsonResult.toString());

		return jsonResult;
	}

	/**
	 * 关闭HTTP Client
	 */
	public void close() {
		if (null != mGroup) {
			try {
				mFuture.sync().channel().closeFuture().sync();
				mGroup.shutdownGracefully();
				mGroup = null;
			} catch (Exception e) {
				LOGGER.error("Close: " + mRemoteHost + ", error: " + e);
			}
		}
	}
}
